/***************************************************************************
 *   This file is part of KTagger                                          *
 *   Copyright (C) 2010 by Luca Bellonda                                   *
 *   lbellonda _at_ gmail.com                                              *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "database.h"
#include "databaseprivate.h"
#include "Utility.h"
#include <QSqlQuery>
#include <QSqlError>
#include <KLocale>
#include "AppLog.h"

#define SOURCE_CLASS "DatabasePersistence"
#define SQL_DRIVER  "QSQLITE"
#define DB_DATA_DIR "dbstorage"
#define DB_FILE_NAME "configuration.db"
#define MAX_LAST_USED_TAGS  "200"

//----------- SQL Commands -----------------

#define SQL_CREATE_TABLE_PROFILE_IF_NOT_EXISTS  ""\
    "create table if not exists PROFILES ( "\
    "id INTEGER PRIMARY KEY AUTOINCREMENT,"\
    "name varchar(64),"\
    "description varchar(128),"\
    "type int,"\
    "creationdate varchar(64),"\
    "updatedate varchar(64), " \
    " enabled int )"

#define SQL_CREATE_TABLE_PROFILE_TAGS_IF_NOT_EXISTS  ""\
    "create table if not exists PROFILE_TAGS ( "\
    "profileId INTEGER,"\
    "tag varchar(256) )"

#define SQL_CREATE_TABLE_PROFILE_ACTIVITIES_IF_NOT_EXISTS  ""\
    "create table if not exists PROFILE_ACTIVITIES ( "\
    "profileId INTEGER,"\
    "activity varchar(256) )"

#define SQL_CREATE_TABLE_CURRENT_CONFIGURATION_IF_NOT_EXISTS  ""\
    "create table if not exists CURRENT_CONFIGURATION ( "\
    "profileId INTEGER,"\
    "updatedate varchar(64) ) "

//----

#define SQL_CREATE_TABLE_TAGS_MRU_IF_NOT_EXISTS  ""\
    "create table if not exists TAG_MRU ( "\
    "tag varchar(256),"\
    "lastuseddate varchar(64) ) "

#define SQL_CREATE_TABLE_TAGS_PROFILES_IF_NOT_EXISTS  ""\
    "create table if not exists TAG_PROFILES ( "\
    "tag_profile varchar(256),"\
    "lastuseddate varchar(64) ) "



DatabasePersistence::DatabasePersistence() : d(new DatabasePersistence::Private(this))
{

}

DatabasePersistence::~DatabasePersistence()
{
    if (NULL != d) {
        delete d;
    }
}

DatabasePersistence::Private::Private(DatabasePersistence *parent)
{
    _parent = parent;
    _errorCode = E_NO_ERROR ;
    _isInited = false;
    initLogger();
}

DatabasePersistence::Private::~Private()
{
}

void DatabasePersistence::Private::initLogger()
{
    logParam.source = SOURCE_CLASS;
}

bool DatabasePersistence::init()
{
    return d->init();
}

bool DatabasePersistence::Private::init()
{
    if (!_isInited) {
        if (!initDB()) {
            return false;
        }
        _isInited = true;
    }
    return true;
}

void DatabasePersistence::Private::setError(const EErrorCode errorCode, const QString &errorMessage)
{
    _errorCode = errorCode;
    _errorMessage = errorMessage ;
    logger.error(&logParam, QString("error %1").arg(errorMessage));
}

DatabasePersistence::EErrorCode DatabasePersistence::getErrorCode()
{
    return d->getErrorCode();
}

DatabasePersistence::EErrorCode DatabasePersistence::Private::getErrorCode()
{
    return _errorCode;
}

bool DatabasePersistence::Private::initDB()
{
    logger.debug(&logParam, QString("DB Started in dir:'%1'").arg(Utility::getLocalDataDir(DB_DATA_DIR)));
    _db = QSqlDatabase::addDatabase(SQL_DRIVER);
    QString dirPath = Utility::getLocalDataDir(DB_DATA_DIR);
    if (NULL == dirPath) {
        return false;
    }
    dirPath.append("/");
    dirPath.append(DB_FILE_NAME);
    _db.setDatabaseName(dirPath) ;
    if (!_db.open()) {
        setError(E_CANTOPEN, i18n("can't open preferences database"));
        return false;
    }
    bool isOk = createTables();
    return isOk ;
}

void DatabasePersistence::Private::setError()
{
    QSqlError error = _db.lastError();
    _errorCode = E_UNKNONN;
    _dbErrorText = error.text() ;
    _dbErrorCode = error.number() ;
    logger.error(&logParam, QString("error %1").arg(_dbErrorText));
}

bool DatabasePersistence::Private::createTables()
{
    QSqlQuery query(_db) ;
    logger.debug(&logParam, "create tables");
    if (!query.exec(SQL_CREATE_TABLE_PROFILE_IF_NOT_EXISTS)) {
        setError();
        logger.error(&logParam, "error creating profile table");
        return false ;
    }
    if (!query.exec(SQL_CREATE_TABLE_PROFILE_TAGS_IF_NOT_EXISTS)) {
        setError();
        logger.error(&logParam, "error creating profile tags table");
        return false ;
    }
    if (!query.exec(SQL_CREATE_TABLE_PROFILE_ACTIVITIES_IF_NOT_EXISTS)) {
        setError();
        logger.error(&logParam, "error creating profile activities table");
        return false ;
    }
    if (!query.exec(SQL_CREATE_TABLE_CURRENT_CONFIGURATION_IF_NOT_EXISTS)) {
        setError();
        logger.error(&logParam, "error creating current profile table");
        return false ;
    }
    if (!query.exec(SQL_CREATE_TABLE_TAGS_MRU_IF_NOT_EXISTS)) {
        setError();
        logger.error(&logParam, "error creating mru tags table");
        return false ;
    }
    if (!query.exec(SQL_CREATE_TABLE_TAGS_PROFILES_IF_NOT_EXISTS)) {
        setError();
        logger.error(&logParam, "error creating tag profiles table");
        return false ;
    }

    logger.debug(&logParam, "create tables end ok");
    return true ;
}

bool DatabasePersistence::insertProfile(TagProfile *profile)
{
    return d->insertProfile(profile);
}

#define SQL_INSERT_PROFILE  ""\
    "insert into PROFILES ( name, description, type, creationdate, updatedate, enabled ) "\
    " values ( "\
    " :name, :description, :type, datetime('now'), datetime('now'), :enabled )"
#define SQL_UPDATE_PROFILE  ""\
    "update PROFILES set name=:name, description=:description, type=:type, updatedate=datetime('now'), enabled=:enabled where id=:id "

#define SQL_INSERT_PROFILE_TAGS  ""\
    "insert into PROFILE_TAGS ( profileId, tag ) "\
    " values ( "\
    " :profileId, :activity )"
#define SQL_INSERT_PROFILE_ACTIVITIES  ""\
    "insert into PROFILE_ACTIVITIES ( profileId, activity ) "\
    " values ( "\
    " :profileId, :activity )"
#define SQL_DELETE_PROFILE_TAGS  ""\
    "delete from PROFILE_TAGS where profileId=:profileId"
#define SQL_DELETE_PROFILE_ACTIVITIES  ""\
    "delete from PROFILE_ACTIVITIES where profileId=:profileId"


bool DatabasePersistence::Private::insertProfileTags(QSqlQuery &query, TagProfile *profile)
{
    query.prepare(SQL_INSERT_PROFILE_TAGS);
    query.bindValue(":profileId", profile->getId());
    foreach(QString tag, profile->getTags()) {
        query.bindValue(":tag", tag);
        if (!query.exec()) {
            setError();
            logger.error(&logParam, "error inserting profile tag");
            return false;
        }
    }
    return true;
}

bool DatabasePersistence::Private::insertProfileActivities(QSqlQuery &query, TagProfile *profile)
{
    query.prepare(SQL_INSERT_PROFILE_ACTIVITIES);
    query.bindValue(":profileId", profile->getId());
    foreach(QString activity, profile->getActivities()) {
        query.bindValue(":activity", activity);
        if (!query.exec()) {
            setError();
            logger.error(&logParam, "error inserting profile activities");
            return false;
        }
    }
    return true;
}


bool DatabasePersistence::Private::insertProfile(TagProfile *profile)
{
    bool isOk = true ;
    logger.debug(&logParam, "insert profile");
    QSqlQuery query(_db) ;

    if (!_db.transaction()) {
        setError();
        logger.error(&logParam, "error starting a transaction");
        isOk = false;
    } else {
        query.prepare(SQL_INSERT_PROFILE);
        query.bindValue(":name", profile->getName());
        query.bindValue(":description", profile->getDescription());
        query.bindValue(":type", profile->getType());
        query.bindValue(":enabled", profile->isEnabled() ? 1 : 0);
        if (!query.exec()) {
            setError();
            logger.error(&logParam, "error inserting profile ");
            isOk = false;
        } else {
            profile->setId(query.lastInsertId().toString());
            if (! insertProfileTags(query, profile)) {
                isOk = false;
            }
            if (isOk) {
                if (! insertProfileActivities(query, profile)) {
                    isOk = false;
                } //- activities
            }
        }
        if (isOk) {
            if (!_db.commit()) {
                setError();
                logger.error(&logParam, "error committing");
                isOk = false;
            }
        } else {
            if (!_db.rollback()) {
                setError();
                logger.error(&logParam, "error rollbacking");
                isOk = false;
            }
        }
    }
    if (isOk) {
        logger.debug(&logParam, "insert profile end ok");
    } else {
        logger.error(&logParam, "insert profile failed");
    }
    return isOk;
}

#define SQL_SELECT_PROFILE_BASE "select id, name, description, type, creationdate, updatedate, enabled from profiles "
#define SQL_SELECT_A_PROFILE_BASE "select id, name, description, type, creationdate, updatedate, enabled from profiles where id= :id "
#define SQL_SELECT_PROFILE_TAGS_BASE "select profileId, tag from PROFILE_TAGS"
#define SQL_SELECT_PROFILE_ACTIVITIES_BASE "select profileId, activity from PROFILE_ACTIVITIES"

void DatabasePersistence::Private::readAProfile(QSqlQuery &query, TagProfile *profile)
{
    profile->setId(query.value(0).toString());
    profile->setName(query.value(1).toString());
    profile->setDescription(query.value(2).toString());
    profile->setType((TagProfile::ESessionType)query.value(3).toInt());
    profile->setCreatedOn(query.value(4).toDateTime());
    profile->setUpdatedOn(query.value(5).toDateTime());
    profile->setEnabled((query.value(6).toInt() != 0) ? true : false);
}

QVector<TagProfile*> *DatabasePersistence::getProfileList()
{
    return d->getProfileList();
}

QVector<TagProfile*> *DatabasePersistence::Private::getProfileList()
{
    return getProfilesEntities(true);
}

QVector<TagProfile*> *DatabasePersistence::getProfileListLight()
{
    return d->getProfileListLight();
}

QVector<TagProfile*> *DatabasePersistence::Private::getProfileListLight()
{
    return getProfilesEntities(false);
}

TagProfile* DatabasePersistence::getProfile(const QString &id)
{
    return d->getProfile(id);
}

TagProfile* DatabasePersistence::Private::getProfile(const QString &id)
{
    TagProfile* profile = NULL ;
    bool isOk = true ;
    logger.debug(&logParam, "read a profile");
    QSqlQuery query(_db) ;
    query.prepare(SQL_SELECT_A_PROFILE_BASE);
    query.bindValue(":id", id);
    if (query.exec()) {
        if (query.next()) {
            profile = new TagProfile();
            if (NULL == profile) {
                isOk = false;
                logger.error(&logParam, "no profile");
            } else  {
                readAProfile(query, profile);
            }
        }
    }
    query.finish();
    if (query.lastError().isValid()) {
        setError();
        isOk = false;
    }
    if (isOk) {
        logger.debug(&logParam, "profile read ok");
    } else {
        logger.error(&logParam, "profiles read nok");
    }
    return profile ;
}


QVector<TagProfile*> *DatabasePersistence::Private::getProfilesEntities(const bool retrieveCompleteProfile)
{
    QHash<QString, TagProfile*> profilesCollection;
    QVector<TagProfile*> *list = new QVector<TagProfile*>();
    if (NULL == list) {
        return NULL ;
    }
    bool isOk = true ;
    logger.debug(&logParam, "read all profiles");
    QSqlQuery query(SQL_SELECT_PROFILE_BASE, _db) ;
    if (query.exec()) {
        while (query.next()) {
            TagProfile *profile = new TagProfile();
            if (NULL == profile) {
                isOk = false;
                logger.error(&logParam, "no profile");
                break;
            }
            readAProfile(query, profile);
            list->append(profile);
            profilesCollection.insert(profile->getId(), profile);
            //privatelist.append(profile);
        }
    }
    query.finish();
    if (query.lastError().isValid()) {
        setError();
        isOk = false;
    }
    if (isOk) {
        logger.debug(&logParam, "profiles read ok");
        if (!retrieveCompleteProfile) {
            return list;
        }
        isOk  = getProfilesTags(profilesCollection);
        if (isOk) {
            isOk  = getProfilesActivities(profilesCollection);
        }
        if (isOk) {
            return list;
        }
    }
    // empty the list
    foreach(TagProfile *profile, profilesCollection) {
        delete profile ;
    }
    logger.error(&logParam, "profiles read nok");
    return NULL;
}

bool DatabasePersistence::Private::getProfilesTags(QHash<QString, TagProfile*> &profilesCollection)
{
    logger.debug(&logParam, "getProfilesTags");
    QSqlQuery query(SQL_SELECT_PROFILE_TAGS_BASE, _db) ;
    while (query.next()) {
        QString id = query.value(0).toString() ;
        QString tag = query.value(1).toString() ;
        TagProfile *profile = profilesCollection.value(id);
        if (NULL != profile) {
            profile->addTag(tag);
        } else {
            logger.warning(&logParam, QString("getProfilesTags: tag without id:").append(id));
        }
    }
    query.finish();
    if (query.lastError().isValid()) {
        logger.error(&logParam, "tags read nok");
        return false;
    }
    logger.debug(&logParam, "tags read ok");
    return true;
}

bool DatabasePersistence::Private::getProfilesActivities(QHash<QString, TagProfile*> &profilesCollection)
{
    logger.debug(&logParam, "getProfilesActivities");
    QSqlQuery query(SQL_SELECT_PROFILE_ACTIVITIES_BASE, _db) ;
    while (query.next()) {
        QString id = query.value(0).toString() ;
        QString tag = query.value(1).toString() ;
        TagProfile *profile = profilesCollection.value(id);
        if (NULL != profile) {
            profile->addActivity(tag);
        } else {
            logger.warning(&logParam, QString("getProfilesActivities: tag without id:").append(id));
        }
    }
    query.finish();
    if (query.lastError().isValid()) {
        logger.error(&logParam, "activities read nok");
        return false;
    }
    logger.debug(&logParam, "activities read ok");
    return true;
}

bool DatabasePersistence::updateProfile(TagProfile *profile)
{
    return d->updateProfile(profile);
}

bool DatabasePersistence::Private::updateProfile(TagProfile *profile)
{
    bool isOk = true ;
    logger.debug(&logParam, "update profile");
    QSqlQuery query(_db) ;

    if (!_db.transaction()) {
        setError();
        logger.error(&logParam, "error starting a transaction");
        isOk = false;
    } else {
        query.prepare(SQL_UPDATE_PROFILE);
        query.bindValue(":id", profile->getId());
        query.bindValue(":name", profile->getName());
        query.bindValue(":description", profile->getDescription());
        query.bindValue(":type", profile->getType());
        query.bindValue(":enabled", profile->isEnabled() ? 1 : 0);
        if (!query.exec()) {
            setError();
            logger.error(&logParam, "error inserting profile");
            isOk = false;
        } else {
            query.prepare(SQL_DELETE_PROFILE_TAGS);
            query.bindValue(":profileId", profile->getId());
            if (! query.exec()) {
                setError();
                isOk = false;
                logger.error(&logParam, "error deleting tags");
            } else {
                if (! insertProfileTags(query, profile)) {
                    isOk = false;
                }
            }
            if (isOk) {
                query.prepare(SQL_DELETE_PROFILE_ACTIVITIES);
                query.bindValue(":profileId", profile->getId());
                if (! query.exec()) {
                    setError();
                    isOk = false;
                    logger.error(&logParam, "error deleting activities");
                } else {
                    if (! insertProfileActivities(query, profile)) {
                        isOk = false;
                    } //- activities
                }
            }
        }
        if (isOk) {
            if (!_db.commit()) {
                setError();
                logger.error(&logParam, "error committing");
                isOk = false;
            }
        } else {
            if (!_db.rollback()) {
                setError();
                logger.error(&logParam, "error rollbacking");
                isOk = false;
            }
        }
    }
    if (isOk) {
        logger.debug(&logParam, "update profile end ok");
    } else {
        logger.error(&logParam, "update profile failed");
    }
    return isOk;
}

bool DatabasePersistence::saveCurrentProfile(TagProfile *profile)
{
    return d->saveCurrentProfile(profile);
}

#define SQL_DEL_CURRENT "delete from CURRENT_CONFIGURATION "
#define SQL_INT_CURRENT "insert into CURRENT_CONFIGURATION ( profileId, updatedate ) values (:profileId, datetime('now') ) "
#define SQL_GET_CURRENT "select profileId from CURRENT_CONFIGURATION "

bool DatabasePersistence::Private::saveCurrentProfile(TagProfile *profile)
{
    bool isOk = true ;
    logger.debug(&logParam, "saveCurrentProfile");

    if (!_db.transaction()) {
        setError();
        logger.error(&logParam, "error starting a transaction");
        isOk = false;
    } else {
        QSqlQuery delQuery(_db) ;
        if (!delQuery.exec(SQL_DEL_CURRENT)) {
            setError();
            isOk = false;
            logger.error(&logParam, "error deleting profile");
        } else {
            QSqlQuery insQuery(_db) ;
            insQuery.prepare(SQL_INT_CURRENT);
            if ((NULL == profile) || (profile->getType() == TagProfile::ALLACTIVITIES)) {
                insQuery.bindValue(":profileId", QVariant());
            } else {
                insQuery.bindValue(":profileId", profile->getId());
            }
            if (!insQuery.exec()) {
                setError();
                isOk = false;
                logger.error(&logParam, "error inserting profile");
            }
        }
    }
    if (isOk) {
        if (!_db.commit()) {
            setError();
            logger.error(&logParam, "error committing");
            isOk = false;
        }
    } else {
        if (!_db.rollback()) {
            setError();
            logger.error(&logParam, "error rollbacking");
            isOk = false;
        }
    }
    if (isOk) {
        logger.debug(&logParam, "saveCurrentProfile end ok");
    } else {
        logger.error(&logParam, "saveCurrentProfile failed");
    }
    return isOk;
}

bool DatabasePersistence::getCurrentProfileId(QString &result)
{
    return d->getCurrentProfileId(result);
}

bool DatabasePersistence::Private::getCurrentProfileId(QString &result)
{
    logger.debug(&logParam, "getCurrentProfile");

    QSqlQuery query(SQL_GET_CURRENT, _db) ;
    while (query.next()) {
        result = query.value(0).toString() ;
        logger.warning(&logParam, QString("getProfilesTags: tag without id:").append(result));
    }
    query.finish();
    if (query.lastError().isValid()) {
        logger.error(&logParam, "getCurrentProfile read nok");
        return false;
    }
    logger.debug(&logParam, "getCurrentProfile read ok");
    return true;
}

#define SQL_DELETE_TAGS_MRU  "delete from TAG_MRU where tag =:tag"
#define SQL_INSERT_TAGS_MRU  "insert into TAG_MRU (tag, lastuseddate ) values (:tag, datetime('now') ) "

bool DatabasePersistence::saveTagMRU(const QString &tag)
{
    return d->saveTagMRU(tag);
}

bool DatabasePersistence::Private::saveTagMRU(const QString &tag)
{
    bool isOk = true ;
    QSqlQuery delQuery(_db) ;
    delQuery.prepare(SQL_DELETE_TAGS_MRU);
    delQuery.bindValue(":tag", tag);
    if (!delQuery.exec()) {
        setError();
        isOk = false;
        logger.error(&logParam, "error deleting tag");
    } else {
        QSqlQuery insQuery(_db) ;
        insQuery.prepare(SQL_INSERT_TAGS_MRU);
        insQuery.bindValue(":tag", tag);
        if (!insQuery.exec()) {
            setError();
            isOk = false;
            logger.error(&logParam, "error deleting profile");
        }
    }
    return isOk ;
}

#define SQL_DELETE_TAGS_CONFIG  "delete from tag_profiles where tag_profile =:profile"
#define SQL_INSERT_TAGS_CONFIG  "insert into tag_profiles (tag_profile, lastuseddate ) values (:profile, datetime('now') ) "


bool DatabasePersistence::saveTagSetConfiguration(const QString &tags)
{
    return d->saveTagSetConfiguration(tags);
}

bool DatabasePersistence::Private::saveTagSetConfiguration(const QString &tags)
{
    bool isOk = true ;
    QSqlQuery delQuery(_db) ;
    delQuery.prepare(SQL_DELETE_TAGS_CONFIG);
    delQuery.bindValue(":profile", tags);
    if (!delQuery.exec()) {
        setError();
        isOk = false;
        logger.error(&logParam, "error deleting tag configuration");
    } else {
        QSqlQuery insQuery(_db) ;
        insQuery.prepare(SQL_INSERT_TAGS_CONFIG);
        insQuery.bindValue(":profile", tags);
        if (!insQuery.exec()) {
            setError();
            isOk = false;
            logger.error(&logParam, "error deleting configuration profile");
        }
    }
    return isOk ;
}

QStringList DatabasePersistence::getLastUsedTags()
{
    return d->getLastUsedTags();
}

#define SQL_READ_TAGS_MRU  "select tag from TAG_MRU order by lastuseddate desc LIMIT " MAX_LAST_USED_TAGS

QStringList DatabasePersistence::Private::getLastUsedTags()
{
    QStringList result;
    logger.debug(&logParam, "getLastUsedTags");
    QSqlQuery query(SQL_READ_TAGS_MRU , _db) ;
    while (query.next()) {
        QString tag = query.value(0).toString() ;
        result.append(tag);
    }
    query.finish();
    if (query.lastError().isValid()) {
        setError();
        logger.error(&logParam, "getLastUsedTags  nok");
    } else {
        logger.debug(&logParam, "getLastUsedTags ok");
    }
    return result ;
}

QStringList DatabasePersistence::getCompanionTags(const QString &tag)
{
    return d->getCompanionTags(tag);
}

#define SQL_READ_COMPANIONTAGS  "select tag_profile from tag_profiles where tag_profile like :tag "

//nb: voglio i valori univoci escluso il tag, provvedo poi a filtrarlo dopo con i tag gia inseriti
QStringList DatabasePersistence::Private::getCompanionTags(const QString &tag)
{
    logger.debug(&logParam, "getCompanionTags enter");
    QString tagToSearch = QString("%%1,%").arg(tag);
    QStringList result;
    QSet<QString> data;
    QSqlQuery query(_db) ;
    query.prepare(SQL_READ_COMPANIONTAGS);
    query.bindValue(":tag", tagToSearch);
    if (query.exec()) {
        while (query.next()) {
            QString tagAll = query.value(0).toString() ;
            QStringList tags = tagAll.split(",");
            foreach(QString tag, tags) {
                if (!data.contains(tag)) {
                    data.insert(tag);
                }
            }
        }
    }
    query.finish();
    foreach(QString value, data) {
        result.append(value);
    }

    if (query.lastError().isValid()) {
        logger.error(&logParam, "getCompanionTags nok");
    } else {
        logger.debug(&logParam, "getCompanionTags ok");
    }
    return result ;
}

QStringList DatabasePersistence::getProfileTags(const QString & id, bool &isOk)
{
    return d->getProfileTags(id, isOk);
}

QStringList DatabasePersistence::Private::getProfileTags(const QString & id, bool &isOk)
{
    logger.debug(&logParam, "getProfileTags");
    QStringList result;
    QSqlQuery query(_db) ;
    query.prepare(SQL_SELECT_PROFILE_TAGS_BASE " where profileId=:id");
    query.bindValue(":id", id);
    if (query.exec()) {
        while (query.next()) {
            QString tag = query.value(1).toString() ;
            result.append(tag);
        }
    }
    query.finish();
    if (query.lastError().isValid()) {
        logger.error(&logParam, "tags read nok");
        isOk = false;
    } else {
        logger.debug(&logParam, "tags read ok");
        isOk = true;
    }
    return result;
}

#define SQL_GET_ACTIVITYTAGS    "select distinct tag from profile_tags pt join profiles p on pt.profileid=p.id and enabled=1 join profile_activities ta on p.id=ta.profileid and ta.activity=:activity"

QStringList DatabasePersistence::getActivityTags(const QString & activityName, bool &isOk)
{
    return d->getActivityTags(activityName, isOk);
}

QStringList DatabasePersistence::Private::getActivityTags(const QString & activityName, bool &isOk)
{
    logger.debug(&logParam, "getActivityTags");
    QStringList result;
    QSqlQuery query(_db) ;
    query.prepare(SQL_GET_ACTIVITYTAGS);
    query.bindValue(":activity", activityName);
    if (query.exec()) {
        while (query.next()) {
            QString tag = query.value(0).toString() ;
            result.append(tag);
        }
    }
    query.finish();
    if (query.lastError().isValid()) {
        logger.error(&logParam, "getActivityTags read nok");
        isOk = false;
    } else {
        logger.debug(&logParam, "getActivityTags read ok");
        isOk = true;
    }
    return result;
}
